package com.UnionPayCenter.distribution.service.impl;

import com.UnionPayCenter.account.entity.Account;
import com.UnionPayCenter.account.entity.PayBill;
import com.UnionPayCenter.account.entity.dto.ChargeDTO;
import com.UnionPayCenter.account.enums.AccountTypeEnum;
import com.UnionPayCenter.account.service.AccountService;
import com.UnionPayCenter.account.service.ChargePayService;
import com.UnionPayCenter.distribution.constants.DistributionConstants;
import com.UnionPayCenter.distribution.constants.SharePropDefaultConstants;
import com.UnionPayCenter.distribution.domain.DistributionUser;
import com.UnionPayCenter.distribution.dto.DistributionBaseDTO;
import com.UnionPayCenter.distribution.dto.DistributionShareDTO;
import com.UnionPayCenter.distribution.dto.DistributionSharePropModifyDTO;
import com.UnionPayCenter.distribution.dto.SharePropModifyDTO;
import com.UnionPayCenter.distribution.entity.*;
import com.UnionPayCenter.distribution.manager.*;
import com.UnionPayCenter.distribution.service.DistributionOperatorService;
import com.UnionPayCenter.distribution.service.DistributionQueryService;
import com.UnionPayCenter.distribution.vo.DistributionTemplateCardVO;
import com.UnionPayCenter.entity.ShopEntity;
import com.UnionPayCenter.entity.UserEntity;
import com.UnionPayCenter.entity.dto.ChargeResponse;
import com.UnionPayCenter.enums.ErrorCodeEnum;
import com.UnionPayCenter.exception.ApiException;
import com.UnionPayCenter.mapper.AccountMapper;
import com.UnionPayCenter.mapper.ShopMapper;
import com.UnionPayCenter.service.ICouponService;
import com.UnionPayCenter.service.IUserService;
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.stream.Collectors;

/**
 * @author Noah
 * @since 1.0.0, 2019/07/02
 */
@Slf4j
@Service
public class DistributionOperatorServiceImpl implements DistributionOperatorService {

    private static ExecutorService executor = Executors.newFixedThreadPool(15);


    @Autowired
    private DistributionTemplateManager templateManager;

    @Autowired
    private SharePropManager sharePropManager;

    @Autowired
    private IUserService userService;

    @Autowired
    private UserDistributionManager userDistributionManager;

    @Autowired
    private UserDistributionRefManager userDistributionRefManager;

    @Autowired
    private AccountService accountService;

    @Autowired
    private DistributionQueryService distributionQueryService;

    @Autowired
    private DistributionSnManager distributionSnManager;

    @Autowired
    private ChargePayService chargePayService;

    @Autowired
    private ShopMapper shopMapper;

    @Autowired
    private AccountMapper accountMapper;

    @Autowired
    private DistributionTemplateCardManager templateCardManager;

    @Autowired
    private ICouponService iCouponService;

    /**
     * 首次上线初始化
     */
    @Override
    public void firstInit() {
        ShopEntity shopEntity = new ShopEntity();
        List<ShopEntity> shopList = shopMapper.select(shopEntity);
        for (ShopEntity shop : shopList) {
            if (Objects.isNull(shop.getShopId())) {
                continue;
            }
            DistributionBaseDTO baseDTO = new DistributionBaseDTO();
            baseDTO.setShopId(shop.getShopId().longValue());
            baseDTO.setBId(shop.getShopId().longValue());
            distributionInitTemplate(baseDTO);
        }
        Account account = new Account();
        List<Account> accountList = accountMapper.selectByCondition(account);
        for (Account aa : accountList) {
            DistributionBaseDTO baseDTO = new DistributionBaseDTO();
            if (Objects.isNull(aa) || Objects.isNull(aa.getShopId()) || Objects.isNull(aa.getUserId())) {
                continue;
            }
            baseDTO.setBId(aa.getShopId().longValue());
            baseDTO.setShopId(aa.getShopId().longValue());
            distributionInitUser(baseDTO, null, aa.getUserId().longValue());
        }


    }

    @Override
    public void distributionInitTemplate(DistributionBaseDTO baseDTO) {
        DistributionTemplateDO templateDO = templateManager.initTemplate(baseDTO);
        sharePropManager.initShareProp(templateDO);
    }


    /**
     * 初始化分享用户信息
     *
     * @param baseDTO            店铺
     * @param userId             分销人ID
     * @param counterpartyUserId 被分销人用户ID
     */
    @Override
    public void distributionInitUser(DistributionBaseDTO baseDTO, Long userId, Long counterpartyUserId) {
        if(Objects.isNull(counterpartyUserId) || counterpartyUserId.equals(userId)){
            return;
        }
        DistributionTemplateDO templateDO = templateManager.queryTemplate(baseDTO);
        if (Objects.isNull(templateDO) || !DistributionConstants.TURE_STRING.equals(templateDO.getFlag())) {
            return;
        }
        UserDistributionRefDO exists = userDistributionRefManager.selectByCounterpartyUser(baseDTO.getBId(), baseDTO.getShopId(), counterpartyUserId);
        final UserDistributionRefDO existsRefDo = converterUserDistributionRefDO(exists);
        doDistributionInitUser(templateDO, userId, counterpartyUserId);
        inviteIncentive(templateDO, existsRefDo, userId, counterpartyUserId);
    }

    private UserDistributionRefDO converterUserDistributionRefDO(UserDistributionRefDO exists){
        if(Objects.isNull(exists)){
            return null;
        }
        UserDistributionRefDO userDistributionRefDO = new UserDistributionRefDO();
        userDistributionRefDO.setIdgen(exists.getIdgen());
        userDistributionRefDO.setInfoSn(exists.getInfoSn());
        userDistributionRefDO.setTIdgen(exists.getTIdgen());
        userDistributionRefDO.setUserId(exists.getUserId());
        userDistributionRefDO.setUserName(exists.getUserName());
        userDistributionRefDO.setUserMobile(exists.getUserMobile());
        userDistributionRefDO.setBId(exists.getBId());
        userDistributionRefDO.setShopId(exists.getShopId());
        userDistributionRefDO.setCounterpartyUserId(exists.getCounterpartyUserId());
        userDistributionRefDO.setCounterpartyUserMobile(exists.getCounterpartyUserMobile());
        userDistributionRefDO.setCounterpartyUserName(exists.getCounterpartyUserName());
        userDistributionRefDO.setShareValTime(exists.getShareValTime());
        userDistributionRefDO.setShareLvl(exists.getShareLvl());
        userDistributionRefDO.setDisType(exists.getDisType());
        userDistributionRefDO.setCreateTime(exists.getCreateTime());
        userDistributionRefDO.setUpdateTime(exists.getUpdateTime());
        userDistributionRefDO.setRemark(exists.getRemark());
        return userDistributionRefDO;
    }

    private void doDistributionInitUser(DistributionTemplateDO templateDO, Long userId, Long counterpartyUserId) {
        if (Objects.isNull(counterpartyUserId) || Objects.isNull(templateDO)) {
            return;
        }
        DistributionUser distributionUser = new DistributionUser();
        distributionUser.setBId(templateDO.getBId());
        distributionUser.setShopId(templateDO.getShopId());
        if (Objects.nonNull(userId)) {
            distributionUser.setUserVo(userService.getUserById(userId.intValue()));
        }
        distributionUser.setCounterpartyUserVo(userService.getUserById(counterpartyUserId.intValue()));
        if (!DistributionConstants.TURE_STRING.equals(templateDO.getFlag())) {
            return;
        }
        Long accountId = getFirstAccountId(String.valueOf(counterpartyUserId), templateDO.getAccType());
        UserDistributionDO userDistributionDO = userDistributionManager.initUserDistribution(distributionUser, accountId);
        if (Objects.isNull(userDistributionDO)) {
            return;
        }
        userDistributionRefManager.initUserDistributionRef(distributionUser, userDistributionDO);

    }

    /**
     * 邀请奖励
     */
    private void inviteIncentive(DistributionTemplateDO templateDO,final  UserDistributionRefDO existsRef, Long userId, Long counterpartyUserId) {
        if (Objects.isNull(templateDO) || Objects.isNull(userId)) {
            return;
        }
        boolean userInvite = false;
        boolean counterpartyUserInvite = false;
        //新建邀请关系或者邀请关系从无到有 则进行奖励
        UserDistributionRefDO newRef = userDistributionRefManager.selectByCounterpartyUser(templateDO.getBId(), templateDO.getShopId(),
                counterpartyUserId);
        if (Objects.isNull(existsRef)) {
            if (Objects.nonNull(newRef)) {
                userInvite = true;
                counterpartyUserInvite = true;
            }
        } else {
            if (Objects.nonNull(newRef) && Objects.isNull(existsRef.getUserId()) && Objects.nonNull(newRef.getUserId())) {
                userInvite = true;
                counterpartyUserInvite = true;
            }
        }
        if (userInvite) {
            List<DistributionTemplateCardDO> userCardList = templateCardManager.listUserTemplateCard(templateDO.getShopId(), templateDO.getIdgen());
            sendInviteIncentive(userCardList, userId, templateDO.getEffectNextDayFlag());
        }
        if (counterpartyUserInvite) {
            List<DistributionTemplateCardDO> counterpartyUserCardList = templateCardManager.
                    listCounterpartyUserTemplateCard(templateDO.getShopId(), templateDO.getIdgen());
            String membershipLevel = templateCardManager.getMemberShipCard(templateDO.getShopId(), templateDO.getIdgen());
            sendInviteIncentive(counterpartyUserCardList, counterpartyUserId, templateDO.getEffectNextDayFlag());
            if (StringUtils.isNotBlank(membershipLevel)) {
                userService.updateUserLevel(counterpartyUserId.intValue(), Integer.valueOf(membershipLevel));
            }
        }

    }


    /**
     * 发送卡券
     *
     * @param cardDOList       卡券列表
     * @param userId           用户ID
     * @param effectiveNextDay 是否次日生效
     */
    private void sendInviteIncentive(List<DistributionTemplateCardDO> cardDOList, Long userId, Integer effectiveNextDay) {
        if (Objects.isNull(userId) || CollectionUtils.isEmpty(cardDOList)) {
            return;
        }
        cardDOList.forEach(e -> {
            iCouponService.insertUserCouponByUserId(e.getShopId().intValue(), userId.intValue(), Integer.valueOf(e.getCardId()), e.getCardNumber(), effectiveNextDay);
        });
    }

    /**
     * 修改分润比例
     *
     * @param modifyDtoList 修改信息
     */
    @Override
    public void updateShareProp(List<SharePropModifyDTO> modifyDtoList) {
        if (CollectionUtils.isEmpty(modifyDtoList)) {
            return;
        }
        modifyDtoList.forEach(e -> sharePropManager.modifyShareProp(e));
    }

    /**
     * 分销
     *
     * @param shareDTO 分销信息
     */
    @Override
    public void distributionShare(DistributionShareDTO shareDTO) {
        log.info("开始分销业务处理，参数内容:{}", JSONObject.toJSONString(shareDTO));
        doDistributionShare(shareDTO);
        log.info("结束分销业务处理");
    }

    /**
     * 分销
     *
     * @param shareDTO 分销信息
     */
    private void doDistributionShare(DistributionShareDTO shareDTO) {
        DistributionTemplateDO templateDO = templateManager.queryTemplate(shareDTO);
        if (Objects.isNull(templateDO) || Objects.isNull(shareDTO) || Objects.isNull(shareDTO.getPayBill())) {
            throw new ApiException(ErrorCodeEnum.DB_TEMPATE_NOT_EXISTS);
        }
        if (!DistributionConstants.TURE_STRING.equals(templateDO.getFlag())) {
            throw new ApiException(ErrorCodeEnum.DB_TEMPATE_CLOSED);
        }
        List<SharePropDO> sharePropDOList = sharePropManager.listByTemplateId(String.valueOf(templateDO.getIdgen()));
        if (CollectionUtils.isEmpty(sharePropDOList)) {
            throw new ApiException(ErrorCodeEnum.DB_SHAREPOP_NOT_EXISTS);
        }
        UserEntity userEntity = userService.getUserById(shareDTO.getPayBill().getUserId());
//        UserDistributionDO distributionDO = userDistributionManager.getPayUserDistributionByUserId(shareDTO.getBId(),
//                shareDTO.getShopId(), shareDTO.getPayBill().getUserId().longValue());
        if (Objects.isNull(userEntity)) {
            return;
        }
        UserDistributionRefDO first = null;
        UserDistributionRefDO second = null;
        List<DistributionSnDO> snDOList = new ArrayList<>();
        switch (templateDO.getShareLvl()) {
            case 1:
                first = userDistributionRefManager.selectByCounterpartyUser(shareDTO.getBId(),
                        shareDTO.getShopId(), shareDTO.getPayBill().getUserId().longValue());

                break;
            case 2:
                first = userDistributionRefManager.selectByCounterpartyUser(shareDTO.getBId(),
                        shareDTO.getShopId(), shareDTO.getPayBill().getUserId().longValue());
                if (Objects.nonNull(first) && Objects.nonNull(first.getUserId())) {
                    second = userDistributionRefManager.selectByCounterpartyUser(shareDTO.getBId(),
                            shareDTO.getShopId(), first.getUserId());
                }
                break;
            default:
                break;
        }
        saveDistributionSn(first, userEntity, shareDTO.getPayBill(), sharePropDOList, SharePropDefaultConstants.FIRST_LVL);
        saveDistributionSn(second, userEntity, shareDTO.getPayBill(), sharePropDOList, SharePropDefaultConstants.SECOND_LVL);
    }

    /**
     * 保存分销流水
     *
     * @param refDO           分销用户信息
     * @param userEntity      对手用户信息
     * @param payBill         支付信息
     * @param sharePropDoList 分销比例
     * @param lvl             分销等级
     */
    private void saveDistributionSn(UserDistributionRefDO refDO, UserEntity userEntity, PayBill payBill,
                                    List<SharePropDO> sharePropDoList, Integer lvl) {
        if (Objects.isNull(refDO) || Objects.isNull(userEntity) || Objects.isNull(refDO.getUserId())) {
            return;
        }
        Date date = new Date();
        SharePropDO propDO = null;
        for (SharePropDO sharePropDO : sharePropDoList) {
            if (lvl.equals(sharePropDO.getLvl())) {
                propDO = sharePropDO;
                break;
            }
        }
        if (Objects.isNull(propDO)) {
            return;
        }

        Integer shareAmount = propDO.getProp().multiply(new BigDecimal(payBill.getAmount())).intValue();
        String sn = saveAccount(refDO.getShopId(), refDO.getUserId(), shareAmount);
        DistributionSnDO distributionSnDO = new DistributionSnDO();
        distributionSnDO.setInfoSn(refDO.getInfoSn());
        distributionSnDO.setUserId(refDO.getUserId());
        distributionSnDO.setBId(refDO.getBId());
        distributionSnDO.setShopId(refDO.getShopId());
        //distributionSnDO.setAccId(refDO.getAccId());
        distributionSnDO.setShareLvl(lvl);
        distributionSnDO.setCreateTime(date);
        distributionSnDO.setUpdateTime(date);
        distributionSnDO.setIdgen(Integer.valueOf(refDO.getIdgen()));
        distributionSnDO.setUserMobile(refDO.getUserMobile());
        distributionSnDO.setUserName(refDO.getUserName());
        distributionSnDO.setCounterpartyUserId(userEntity.getUserId().longValue());
        distributionSnDO.setCounterpartyUserMobile(userEntity.getMobile());
        distributionSnDO.setCounterpartyUserName(userEntity.getNick());
        distributionSnDO.setTrTime(payBill.getUpdateTime());
        distributionSnDO.setActualAmount(payBill.getAmount());
        distributionSnDO.setShareAmount(shareAmount);
        distributionSnDO.setSn(sn);
        distributionSnDO.setShareShType("2");
        distributionSnDO.setShareSn(payBill.getSn());
        distributionSnManager.saveDistributionSn(distributionSnDO);
    }


    /**
     * 保存至分销账户
     *
     * @param shopId 店铺id
     * @param userId 用户ID
     * @param amount 金额
     * @return
     */
    private String saveAccount(Long shopId, Long userId, Integer amount) {
        ChargeDTO chargeDTO = new ChargeDTO();
        chargeDTO.setShopId(shopId.intValue());
        chargeDTO.setUserId(userId.intValue());
        chargeDTO.setChargeDirect(true);
        ChargeResponse response = chargePayService.chargeRewardsCash(chargeDTO, amount, AccountTypeEnum.DISTRIBUTION_CHARGE);
        if (null == response) {
            return null;
        }
        return response.getSn();
    }

    /**
     * 获取首选账户
     *
     * @param userId 用户ID
     * @return 账户ID
     */
    private Long getFirstAccountId(String userId, String type) {
        return null;
//        type = StringUtils.isBlank(type) ? String.valueOf(AccountTypeEnum.DISTRIBUTION_CHARGE.code()) : type;
//        Account account = accountService.getAccountByUserId(Integer.valueOf(userId), AccountTypeEnum.getEnum(Integer.valueOf(type)));
//        if (Objects.isNull(account) || Objects.isNull(account.getAccountId())) {
//            throw new ApiException(TK2001);
//        }
//        return account.getAccountId();
    }

    /**
     * 更新分销模板状态
     *
     * @param baseDTO 基本信息
     * @param flag    标准
     */
    private void updateDistributionTemplateFlag(DistributionBaseDTO baseDTO, String flag) {
        flag = StringUtils.isBlank(flag) ? DistributionConstants.TURE_STRING : flag;
        if (baseDTO.getShopId() == null) {
            baseDTO.setShopId(baseDTO.getBId());
        }
        templateManager.updateTemplateFlag(baseDTO, flag);
    }

    /**
     * 更新模板信息
     *
     * @param propModifyDTO
     */
    @Override
    public void updateDistributionTemplate(DistributionSharePropModifyDTO propModifyDTO) {
        DistributionTemplateDO templateDO = new DistributionTemplateDO();
        templateDO.setFlag(propModifyDTO.getFlag());
        templateDO.setEffectNextDayFlag(Objects.isNull(propModifyDTO.getEffectNextDay()) ? DistributionConstants.FALSE_NUMBER : Integer.valueOf(propModifyDTO.getEffectNextDay()));
        if (propModifyDTO.getShopId() == null) {
            propModifyDTO.setShopId(propModifyDTO.getBId());
        }
        updateShareProp(propModifyDTO.getSharePropModifyDTOList());
        templateDO = templateManager.updateDistributionTemplate(templateDO, propModifyDTO);
        if (Objects.isNull(templateDO)) {
            return;
        }
        templateCardManager.saveUserDistributionTemplateCard(converterTemplateCardList(propModifyDTO.getUserIncentiveCardList(), templateDO.getShopId(), templateDO.getIdgen()),
                templateDO.getShopId(), templateDO.getIdgen());
        templateCardManager.saveCounterpartyUserDistributionTemplateCard(converterTemplateCardList(propModifyDTO.getCounterpartyUserIncentiveCardList(), templateDO.getShopId(), templateDO.getIdgen()),
                propModifyDTO.getCounterpartyLevelId(), templateDO.getShopId(), templateDO.getIdgen());
    }

    private List<DistributionTemplateCardDO> converterTemplateCardList(List<DistributionTemplateCardVO> cardVOList, Long shopId, Integer idgen) {
        if (CollectionUtils.isEmpty(cardVOList)) {
            return Collections.emptyList();
        }
        return cardVOList.stream().map(e -> {
            DistributionTemplateCardDO cardDO = new DistributionTemplateCardDO();
            cardDO.setCardId(String.valueOf(e.getCardId()));
            cardDO.setShopId(shopId);
            cardDO.setIdgen(idgen);
            cardDO.setCardNumber(e.getCardNumber());
            return cardDO;
        }).collect(Collectors.toList());
    }
}
